using System;
using System.Linq.Expressions;

namespace CommonServiceManager.Configuration
{
	/// <summary>
	/// The configurator enables configuration options for one type in the underlying IoC Container. Each method reaturns this making method chaining possible.
	/// </summary>
	/// <typeparam name="TImplementation">The <see cref="Type"/> we are configuring.</typeparam>
	public interface IConfigurator<TImplementation>
	{
		/// <summary>
		/// The [optional] name of the implementation to configure
		/// </summary>
		string Name { get; }

		/// <summary>
		/// Set the <typeparamref name="TImplementation"/> as singleton instance in the application.
		/// </summary>
		/// <returns>This <see cref="IConfigurator{TImplementation}"/> for a fluent interface.</returns>
		/// <exception cref="ConfigurationException">if there is are errors configuring the <typeparamref name="TImplementation"/>.</exception>
		IConfigurator<TImplementation> AsSingleton();

		/// <summary>
		/// Use a type-safe expression to select the constructor to use for Dependency Injection.
		/// </summary>
		/// <param name="constructionExpression">An <see cref="Expression{TDelegate}"/> which will be parsed.</param>
		/// <param name="sequentialImplementationKeys">If any of the parameter types in the constructor has more than one implementation this may also be defined.</param>
		/// <exception cref="MalformedConfigurationExpressionException">Exception is thrown if the <see cref="Expression{TDelegate}"/> is not parsable as an expression that returns a new instance of <typeparamref name="TImplementation"/></exception>
		/// <exception cref="ConfigurationException">if there is are errors configuring the <typeparamref name="TImplementation"/>.</exception>
		/// <remarks>The expressions will be parsed only - never evalutated.</remarks>
		/// <remarks>The <paramref name="sequentialImplementationKeys"/> will not be type safe during refactoring and mark sequentially the (optional) implementation key used to register a speciffic implementation of a type that is a parameter to the constructor selected for Dependency Injection.</remarks>
		/// <returns>This <see cref="IConfigurator{TImplementation}"/> for a fluent interface.</returns>
		IConfigurator<TImplementation> InjectionConstructor(Expression<Func<TImplementation>> constructionExpression, params string[] sequentialImplementationKeys);

		/// <summary>
		/// Select properties the DI container should choose when injecting the instance of <typeparamref name="TImplementation"/>
		/// </summary>
		/// <param name="injectionPropertyExpressions">Lambda expressions defining in a strongly typed manner the property member that is to be marked for property injection</param>
		/// <remarks>The expressions will be parsed only - never evaluated.</remarks>
		/// <exception cref="MalformedConfigurationExpressionException">Exception is thrown if the <see cref="Expression{TDelegate}"/> is not parsable as an expression that selects a property on an instance of <typeparamref name="TImplementation"/></exception>
		/// <exception cref="ConfigurationException">if there is are errors configuring the <typeparamref name="TImplementation"/>.</exception>
		/// <returns>This <see cref="IConfigurator{TImplementation}"/> for a fluent interface.</returns>
		IConfigurator<TImplementation> InjectionProperties(params Expression<Func<TImplementation, object>>[] injectionPropertyExpressions);

		/// <summary>
		/// Use type-safe expressions to select properties for Dependency Injection. Pair each up with an implmementation key when a property has more than one implementation in the system.
		/// </summary>
		/// <param name="keyedPropertyInjectionPropertyExpressions">The expressions that each selects a property for dependency injection together with an implementation key for the <see cref="Type"/> of the property.</param>
		/// <remarks>The expressions will be parsed only - never evalutated.</remarks>
		/// <exception cref="MalformedConfigurationExpressionException">Exception is thrown if the <see cref="Expression{TDelegate}"/> is not parsable as an expression that selects a property on an instance of <typeparamref name="TImplementation"/></exception>
		/// <exception cref="ConfigurationException">if there is are errors configuring the <typeparamref name="TImplementation"/>.</exception>
		/// <returns>This <see cref="IConfigurator{TImplementation}"/> for a fluent interface.</returns>
		IConfigurator<TImplementation> InjectionProperties(params KeyedPropertyExpression<TImplementation>[] keyedPropertyInjectionPropertyExpressions);
	}
}